package com.lj.auth.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ICaptcha;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.lj.auth.params.LoginParams;
import com.lj.auth.params.PhoneLoginParams;
import com.lj.auth.service.SysAuthService;
import com.lj.common.exception.CommonException;
import com.lj.common.utils.CheckUtils;
import com.lj.common.utils.RedisUtil;
import com.lj.sys.entity.SysUser;
import com.lj.sys.enums.SysUserStatus;
import com.lj.sys.result.LoginUserInfo;
import com.lj.sys.service.*;
import com.lj.sys.utils.SysUserUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

/**
 * @author luojing
 * @since 2024/10/29 10:26
 */
@Service
public class SysAuthServiceImpl implements SysAuthService {

    @Resource
    private RedisUtil redisUtil;
    @Resource
    private SysUserService userService;
    @Resource
    private SysUserRoleService userRoleService;
    @Resource
    private SysMenuService sysMenuService;
    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private SysConfigService configService;
    @Resource
    private SysDeptService deptService;

    /**
     * 验证码redis前缀
     */
    private static final String CAPTCHA_PREFIX = "captcha:";
    /**
     * 手机验证码前缀
     */
    private static final String PHONE_CAPTCHA_PREFIX = "phone:";
    /**
     * 验证码过期时间 单位分钟
     */
    private static final Integer CAPTCHA_EXPIRES = 5;

    @Override
    public void captcha(String uuid, OutputStream out) {
        CheckUtils.ifBlank(uuid, "uuid不能为空");
        //定义图形验证码的长和宽
        ICaptcha captcha = CaptchaUtil.createLineCaptcha(200, 40);
        String code = captcha.getCode();
        // 将验证码存入redis,并设置过期时间
        if (!redisUtil.set(CAPTCHA_PREFIX + uuid, code, CAPTCHA_EXPIRES, TimeUnit.MINUTES)) {
            throw new CommonException("无法获取验证码！请联系管理员！");
        }
        captcha.write(out);
    }

    @Override
    public boolean validate(String uuid, String captcha) {
        return validate("", uuid, captcha);
    }

    @Override
    public boolean validate(String prefix, String uuid, String captcha) {
        CheckUtils.ifBlank(uuid, "uuid不能为空");
        String key = CAPTCHA_PREFIX + prefix + uuid;
        String code = redisUtil.get(key);
        CheckUtils.ifBlank(code, "验证码已失效!");
        if (StrUtil.isBlank(prefix)) {
            // 普通的账号密码登录方式删除验证码
            redisUtil.delete(key);
        }
        // 对比
        return StrUtil.equalsIgnoreCase(code, captcha);
    }

    @Override
    public SaTokenInfo login(LoginParams params) {
        // 校验验证码 (如果系统配置的不校验验证码会直接跳过)
        if (Boolean.TRUE.equals(configService.getBooleanValue("VALIDATE_CAPTCHA")) &&
                !validate(params.getUuid(), params.getCaptcha())) {
            throw new CommonException("验证码不正确!");
        }
        SysUser sysUser = userService.getUserByUserName(params.getUserName());
        if (sysUser == null || !StrUtil.equalsIgnoreCase(sysUser.getPassword(), SaSecureUtil.sha256(params.getPassword()))) {
            throw new CommonException("用户名或密码不正确!");
        }
        return doLogin(sysUser);
    }

    @Override
    public void phoneCaptcha(String phone) {
        CheckUtils.ifBlank(phone, "手机号不能为空");
        String code = RandomUtil.randomNumbers(6);
        // 将验证码存入redis,并设置过期时间
        if (!redisUtil.set(CAPTCHA_PREFIX + PHONE_CAPTCHA_PREFIX + phone, code, CAPTCHA_EXPIRES, TimeUnit.MINUTES)) {
            throw new CommonException("无法获取验证码！请联系管理员！");
        }
        // todo 发送短信验证码
    }

    @Override
    public SaTokenInfo loginByPhone(PhoneLoginParams params) {
        // 校验短信验证码
        if (!validate(PHONE_CAPTCHA_PREFIX, params.getPhone(), params.getCaptcha())) {
            throw new CommonException("验证码不正确!");
        }
        SysUser sysUser = userService.getUserByMobile(params.getPhone());
        return doLogin(sysUser);
    }

    @Override
    public SaTokenInfo doLogin(Long sysUserId) {
        return this.doLogin(userService.getById(sysUserId));
    }

    @Override
    public SaTokenInfo doLogin(SysUser sysUser) {
        CheckUtils.ifNull(sysUser, "用户不存在!");
        // 判断用户状态
        CheckUtils.ifCondition(!SysUserStatus.NORMAL.equals(sysUser.getUserStatus()),
                "当前账号处于 [" + sysUser.getUserStatus().getDesc() + "] 状态！");
        // 真实登录
        StpUtil.login(sysUser.getId());
        // 设置缓存用户的信息
        SysUserUtils.setUserInfo(getUserInfo(sysUser));
        // 获取token信息
        return StpUtil.getTokenInfo();
    }

    private LoginUserInfo getUserInfo(SysUser sysUser) {
        LoginUserInfo loginUserInfo = new LoginUserInfo(sysUser);
        loginUserInfo.setDeptIdList(deptService.allDeptId());
        loginUserInfo.setRoleIdList(userRoleService.getRoleIdByUserId(sysUser.getId()));
        loginUserInfo.setPermissionList(sysMenuService.getAllPermission(sysUser.getId()).stream().toList());
        loginUserInfo.setRoleCodeList(sysRoleService.getRoleCode(sysUser.getId()));
        return loginUserInfo;
    }

    @Override
    public void logout() {
        StpUtil.logout();
    }
}
